/*
 * @Author: 13177709051 3506463075@qq.com
 * @Date: 2024-10-25 17:15:11
 * @LastEditors: 13177709051 3506463075@qq.com
 * @LastEditTime: 2024-10-25 20:31:26
 * @FilePath: /linux24/testsig.cc
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
#include <iostream>
#include <cstdlib>
#include <sys/types.h>
#include <signal.h>
#include <unistd.h>
#include <sys/wait.h>

#include <stdio.h>
#include <signal.h>

#include <stdio.h>
#include <signal.h>

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void handler(int sig)
{
    pid_t id;
    while ((id = waitpid(-1, NULL, WNOHANG)) > 0)
    {
        printf("wait child success: %d\n", id);
    }
    printf("child is quit! %d\n", getpid());
}

int main()
{
    signal(SIGCHLD, handler);
    pid_t cid;
    if ((cid = fork()) == 0)
    { // child
        printf("child : %d\n", getpid());
        sleep(3);
        exit(1);
    }
    while (1)
    {
        printf("father proc is doing some thing!\n");
        sleep(1);
    }
    return 0;
}

// #include <stdio.h>
// #include <signal.h>
// volatile int flag = 0;
// void handler(int sig)
// {
//     printf("chage flag 0 to 1\n");
//     flag = 1;
// }
// int main()
// {
//     signal(2, handler);
//     while (!flag)
//         ;
//     printf("process quit normal\n");
//     return 0;
// }

// int flag = 0;
// void handler(int sig)
// {
//     printf("chage flag 0 to 1\n");
//     flag = 1;
// }
// int main()
// {
//     signal(2, handler);
//     while (!flag);
//     printf("process quit normal\n");
//     return 0;
// }

// #include <signal.h>
// int sigaction(int signo, const struct sigaction *act, struct sigaction *oact)

// 函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含 任何有
// 效信号。
// 函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示 该信号集的有效信号包括系
// 统支持的所有信号。
// 注意,在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的
// 状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号.

// #include <signal.h>
// int sigemptyset(sigset_t *set);
// int sigfillset(sigset_t *set);
// int sigaddset (sigset_t *set, int signo);
// int sigdelset(sigset_t *set, int signo);
// int sigismember（const sigset_t *set, int signo);

// sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态,至于这个类型内部如何存储这些bit则依赖于系统
// 实现,从使用者的角度是不必关心的,使用者只能调用以下函数来操作sigset_ t变量,而不应该对它的内部数据做
// 任何解释,比如用printf直接打印sigset_t变量是没有意义的

// 从上图来看,每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。
// 因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号
// 的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有
// 效”和“无效”的含义是该信号是否处于未决状态。下一节将详细介绍信号集的各种操作。 阻塞信号集也叫做当
// 前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。

// 每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号
// 产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子
// 中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
// SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前
// 不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
// SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。
// 如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次
// 或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可
// 以依次放在一个队列里。本章不讨论实时信号

// 实际执行信号的处理动作称为信号递达(Delivery)
// 信号从产生到递达之间的状态,称为信号未决(Pending)。
// 进程可以选择阻塞 (Block )某个信号。
// 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
// 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
// void handler(int sig)
// {
//     printf("catch a sig : %d\n", sig);
// }

// int main()
// {
//     signal(2, handler);
//     while(1)
//     {
//         sleep(1);
//         printf("hello! I am ctrl + c\n");
//     }
//     return 0;
// }

// int Sum(int start, int end)
// {
//     sleep(5);
//     int sum = 0;
//     for (int i = start; i <= end; i++)
//     {
//         sum /= 0; // core
//         sum += i;
//     }
//     return sum;
// }

// int main()
// {
//     pid_t id = fork();
//     if (id == 0)
//     {
//         sleep(1);
//         // child
//         Sum(0, 100);
//         exit(0);
//     }
//     // father
//     int status = 0;
//     pid_t rid = waitpid(id, &status, 0);
//     if (rid == id)
//     {
//         printf("exit code: %d, exit sig: %d, core dump: %d\n", (status >> 8) & 0xFF, status & 0x7F, (status >> 7) & 0x1);
//     }
//     return 0;
// }

// 当 Sum 函数由于除以零而崩溃时，操作系统会发送一个信号（如 SIGFPE）给子进程。
// 父进程通过 waitpid 等待子进程，并获取它的退出状态。
// 输出的 exit sig 会指示该进程是由于信号崩溃的，core dump 将指示是否生成了核心转储。

// 在计算机术语中，“Core”常指“核心转储”或“核心转储文件”（core dump），是指在程序崩溃或异常终止时，操作系统将该程序的内存内容保存到一个文件中的过程。
// 核心转储文件包含了程序在崩溃时的内存状态，包括堆栈、寄存器内容和进程状态。开发者可以使用这些信息来分析程序错误，进行调试和故障排查。
// 核心转储通常由操作系统自动生成，用户和开发者可以使用调试工具（如 gdb）来分析这些转储文件，从而了解程序崩溃的原因。

// void handler(int sig)
// {
//     sleep(2);
//     std::cout << "get a sig: " << sig << std::endl;
// }

// int main()
// {
//     // signal(SIGSEGV, handler);
//     // signal(SIGFPE, handler);

//     int *p = nullptr;
//     *p = 100; // SIGSEGV
//     int a = 10;
//     a /= 0; // 8) SIGFPE
//     while (true)
//     {
//         std::cout << "hello signal, pid: " << getpid() << std::endl;
//         sleep(1);
//     }
// }

// 程序为什么会崩溃？？？非法访问、操作(?), 导致OS给进程发送信号啦！！ --- 为什么
// signal(SIGSEGV, handler);
// signal(SIGFPE, handler);
//  崩溃了为什么会退出？默认是终止进程
//  可以不退出吗？可以，捕捉了异常, 推荐终止进程(为什么？) --- 为什么？

// int cnt = 1;

// void handler(int sig)
// {
//     alarm(1);
//     std::cout << "cnt: " << cnt << " get a sig: " << sig << std::endl;
// }

// int main()
// {
//     signal(SIGALRM, handler);

//     alarm(1); // 设定1S后的闹钟 -- 1S --- SIGALRM
//     while (true)
//     {
//         std::cout << "cnt: " << cnt << std::endl;
//         cnt++;
//         sleep(1);
//     }
// }

// 每当定时器到期（即每隔 1 秒），程序发送 SIGALRM 信号。
// handler 函数被调用，输出当前的 cnt 值和信号编号，然后重设定时器以在下一秒发送信号。

// void handler(int sig)
// {
//     std::cout << "get a sig: " << sig << std::endl;
// }

// int main()
// {
//     // int cnt = 0;
//     // signal(SIGABRT, handler);
//     for (int i = 1; i <= 31; i++)
//         signal(i, handler);

//     while (true)
//     {
//         sleep(1);
//         std::cout << "hello sig, pid: " << getpid() << std::endl;

//         // raise(3);
//         // abort();
//     }
// }